//text 语法模板定义
package main

import (
	"strings"
	"text/template"
	"time"
)

var (
	idlpackagename string
	//工具函数
	funcsMap = template.FuncMap{"idltime": TimeFormat,
		"stfieldup":  StFieldFormat,
		"getidlname": GetIdlName,
		"tolower":    strings.ToLower,
	}
)

const (
	//@title 类型定义模板
	typeletter = `{{- define "typelet"}}
{{- if eq .GoType "map"}}
{{- if .Value}} map[{{.Key.GoType}}]{{if .Value.IsStruct}}{{"*"}}{{getidlname}}{{end}}{{.Value.GoType}}
{{- else}} map[{{.Key.GoType}}]bool{{end}}
{{- else if eq .GoType "[]"}} []{{if .Key.IsStruct}}{{"*"}}{{getidlname}}{{end}}{{.Key.GoType}}
{{- else}} {{if .IsStruct}}*{{getidlname}}{{end}}{{.GoType}}{{end}}
{{- end}}`

	//@title 结构体生成
	stblock = `{{- define "stblock"}}type {{.Name}} struct{
{{- range $index,$elem := .Fields}}
	{{stfieldup .Name}} {{block "typelet" $elem}}{{end}}
{{- end}}
}
{{end}}
`
	//@title 结构体文件生成模板
	stletter = `
// Generated by the go idl tools. DO NOT EDIT {{idltime}}
// source: {{.IdlName}}

package idldata
import "{{.IdlName}}/idldata/pbdata"

{{range .Structs}}
{{- block "stblock" .}}{{end}}

func(this *{{.Name}}) SerializeToPb() *pbdata.{{.Name}}{
	pbobj := &pbdata.{{.Name}}{
	}
	{{- range .Fields }}
	{{- $fn := stfieldup .Name}}
	{{- if eq .IdlType "i8" "i16"}}
	pbobj.{{$fn}} = int32(this.{{$fn}})
	{{- else if eq .IdlType "ui8" "ui16"}}
	pbobj.{{$fn}} = uint32(this.{{$fn}})
	{{- else if eq .IdlType "set" }}
	for k, _ := range this.{{$fn}} {
		pbobj.{{$fn}} = append(pbobj.{{$fn}}, k)
	}
	{{- else if eq .IdlType "seq"}}
	for _,v := range this.{{$fn}} {
		{{- if .Key.IsStruct}}
		pbobj.{{$fn}} = append(pbobj.{{$fn}}, v.SerializeToPb())
		{{- else}}
		pbobj.{{$fn}} = append(pbobj.{{$fn}}, v)
		{{- end}}
	}
	{{- else if eq .IdlType "dict"}}
	if pbobj.{{$fn}} == nil{
		pbobj.{{$fn}}=make(map[{{.Key.GoType}}]{{if .Value.IsStruct}}*pbdata.{{end}}{{.Value.GoType}})
	}
	for k,v := range this.{{$fn}} {
		{{- if .Value.IsStruct}}
		pbobj.{{$fn}}[k] = v.SerializeToPb()
		{{- else}}
		pbobj.{{$fn}}[k] = v
		{{- end}}
	}
	{{- else if .IsStruct}}
	pbobj.{{$fn}} = this.{{$fn}}.SerializeToPb()
	{{- else}}
	pbobj.{{$fn}} = this.{{$fn}}
	{{- end}}
	{{- end}}
	return pbobj
}

func (this *{{.Name}}) ParseFromPb(pbobj *pbdata.{{.Name}}) {
	if pbobj == nil {
		return
	}
	{{- range .Fields }}
	{{- $fn := stfieldup .Name}}
	{{- if eq .IdlType "i8" "i16" "ui8" "ui16"}}
	this.{{$fn}} = {{.GoType}}(pbobj.{{$fn}})
	{{- else if eq .IdlType "set" }}
	if this.{{$fn}} == nil{
		this.{{$fn}}=make(map[{{.Key.GoType}}]bool)
	}
	for _,v := range pbobj.{{$fn}} {
		this.{{$fn}}[v] = true
	}
	{{- else if eq .IdlType "seq"}}
	for _,v := range pbobj.{{$fn}} {
		{{- if .Key.IsStruct}}
		obj := &{{.Key.GoType}}{}
		obj.ParseFromPb(v)
		this.{{$fn}} = append(this.{{$fn}}, obj)
		{{- else}}
		this.{{$fn}} = append(this.{{$fn}}, {{.Key.GoType}}(v))
		{{- end}}
	}
	{{- else if eq .IdlType "dict"}}
	if this.{{$fn}} == nil{
		this.{{$fn}} = make(map[{{.Key.GoType}}]{{if .Value.IsStruct}}*{{end}}{{.Value.GoType}})
	}
	for k,v := range pbobj.{{$fn}} {
		{{- if .Value.IsStruct}}
		this.{{$fn}}[k].ParseFromPb(v)
		{{- else}}
		this.{{$fn}}[k] = {{.Value.GoType}}(v)
		{{- end}}
	}
	{{- else if .IsStruct}}
	this.{{$fn}} = &{{.GoType}}{}
	this.{{$fn}}.ParseFromPb(pbobj.{{$fn}})
	{{- else}}
	this.{{$fn}} = pbobj.{{$fn}}
	{{- end}}
	{{- end}}
}
{{end}}
`

	//@title 函数模板
	funcletter = `{{define "methods"}}{{range .}}
	{{stfieldup .Name}}(
{{- range $index,$elem := .Arguments}}
{{- if $index }}{{print ", "}}{{end}} 
{{- if ne $elem.GoType "void" }}
{{- block "typelet" $elem}}{{end}}
{{- end}}
{{- end}})({{if .RetType}}{{if ne .RetType.GoType "void" }}{{block "typelet" .RetType}}{{end}},{{end}}error{{end}})
{{- end}}
{{- end}}
`
)

//生成返回值类型
func TimeFormat() string {
	return time.Now().Format("2006-01-02 15:04:05")
}

// go 语言用大小写来控制 是不是暴露给包外面使用 所以 我需要把首字符 转换为大写......哭唧唧
func StFieldFormat(srcfield string) string {
	Fst := strings.ToUpper(srcfield[0:1])
	return Fst + srcfield[1:]
}

func GetIdlName() string {
	if len(idlpackagename) != 0 {
		return idlpackagename + "."
	}
	return ""
}
